package com.example.demo.leetcode.file;

import java.io.*;
import java.util.*;

public class DivideTreeSetHandler {

    /**
     * 排序
     * @param path 数据文件路径
     * @param size 每个数据文件的大小（行数）
     */
    public List<String> divide(String path, int size){
        long t1 = System.currentTimeMillis();
        File file = new File(path);
        if(!file.exists())
            return null;

        List<String> outs = new ArrayList<String>();

        try(BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8"));){
            int fileNo = 0; // 临时文件编号
            Set<Integer> set = new TreeSet<Integer>();
            while(true){
                String line = reader.readLine();

                // 读取结束！
                if(line == null){
                    writeSetToTmpFile(set, fileNo, outs);
                    break;
                }

                // 空行，跳过
                if("".equals(line.trim())){
                    continue;
                }

                set.add(new Integer(line));

                // 数据量达到:
                if(set.size() >= size){
                    writeSetToTmpFile(set, fileNo, outs);
                    fileNo ++;
                }
            }

        }catch(Exception e){
            e.printStackTrace();
        }
        long t2 = System.currentTimeMillis();

        System.out.println("拆分完成！耗时：" + (t2 - t1) + "毫秒。");

        return outs;
    }

    // set数据写入到文件中：
    private void writeSetToTmpFile(Set<Integer> set, int fileNo, List<String> outs) {
        long t1 = System.currentTimeMillis();
        File file = new File("tmp_" + fileNo);
        if(file.exists())
            file.delete();

        try (PrintWriter writer = new PrintWriter(file, "UTF-8");) {
            Iterator<Integer> iterator = set.iterator();
            while(iterator.hasNext()){
                writer.write(iterator.next() + "\n");
            }
            set.clear();
        }catch (Exception e){
            e.printStackTrace();
        }
        long t2 = System.currentTimeMillis();
        System.out.println("生成临时文件：" + file.getAbsolutePath() + "！耗时：" + (t2 - t1) + "毫秒。");
        outs.add(file.getAbsolutePath());
    }

    /**
     * 合并数据
     * @param ins
     */
    public String combine(List<String> ins) {
        long t1 = System.currentTimeMillis();

        if(ins == null || ins.size() <= 1)
            return null;

        File file = new File("tmp");
        if(file.exists())
            file.delete();

        try(PrintWriter writer = new PrintWriter(file, "UTF-8");){
            List<BufferedReader> readers = new ArrayList<>();
            for (String in : ins) {
                readers.add(new BufferedReader(new InputStreamReader(new FileInputStream(in),"UTF-8")));
            }

            while(readers.size() > 0){
                BufferedReader reader0 = readers.get(0);//从第一个文件开始取数
                while(true){  //这个循环为了遍历文件中的所有数字
                    String line = reader0.readLine();
                    if(line == null){
                        readers.remove(0);
                        break;
                    }
                    if("".equals(line.trim()))
                        continue;

                    // 用个set记录从多个文件中取出的数据，这些数据需要继续排序：
                    Set<Integer> set = new TreeSet<Integer>();

                    int data = new Integer(line); //获取一个数data

                    // 先把data放入set：
                    set.add(data);
                    //从后面往前遍历到第二个文件, 取出所有比data小的数，从小到大排序后放入文件中
                    //相当于第一个文件的每个数，和后面的所有文件进行一次归并
                    //如果第一个文件全部取完没有结束，那么继续第二个文件和后面所有文件归并
                    for(int i = readers.size() - 1; i > 0; i--){
                        BufferedReader readeri = readers.get(i);
                        while(true){
                            // 设置一个标记，如果后边datai大于data了，需要reset到此处！
                            readeri.mark(1024);

                            String linei = readeri.readLine();
                            if(linei == null){
                                readers.remove(i);
                                break;
                            }
                            if("".equals(linei.trim()))
                                continue;

                            int datai = new Integer(linei);

                            // datai小于data，则把datai放入set，会自动排序
                            if(datai < data){
                                set.add(datai);
                            }
                            // datai等于data，则暂时退出，停止读取
                            else if(datai == data){
                                break;
                            }
                            // datai大于data，则往回退一行（退到标记处），停止读取
                            else{
                                readeri.reset();
                                break;
                            }
                        }
                    }

                    // 按data查找，小于data的值，都已经存入set了，此时把set输出到文件中：
                    Iterator<Integer> iterator = set.iterator();
                    while(iterator.hasNext()){
                        writer.write(iterator.next() + "\n");
                    }
                    set.clear();
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }

        long t2 = System.currentTimeMillis();
        System.out.println("合并完成！耗时：" + (t2 - t1) + "毫秒。");

        return file.getAbsolutePath();
    }

    /**
     * 从pos开始，获取count个数
     * @param pos
     * @param count
     * @return
     */
    public Integer[] limit(int pos, int count, String path){
        // TODO : 从排序后的文件中读取数据即可！不写了！
        Integer [] limit = new Integer[count];
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(path), "UTF-8"));
            for(int i=0;i<pos;i++){
                reader.readLine();
            }
            for(int i = 0 ; i<count;i++){
                limit[i] = new Integer(reader.readLine());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return limit;
    }

    // 测试：
    public static void main(String[] args) {
        // 数据量：
        int dataCount = 100000000;
        // 分页数（拆分文件数）：
        int pageCount = 10;
        // 每页数据量：
        int perPageCount = dataCount / pageCount;

        // 生成一亿数据：
        DataProducer.produce(dataCount, "data");

        DivideTreeSetHandler handler = new DivideTreeSetHandler();

        // 拆分排序：返回拆分完的文件路径集合(此处生成的文件是有序的)
        List<String> tmps = handler.divide("data", perPageCount);

        // 合并排序：
        String tmp = handler.combine(tmps);

        // 获取数据：
        Integer[] limit = handler.limit(10000, 100, tmp);
        System.out.println(limit);
    }


}
